﻿using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Linq.Expressions;
using WebShop.DAL.Interfaces;
using Webdiyer.WebControls.Mvc;

namespace WebShop.DAL.Repositories
{
    public abstract class EntityRepositoryUnit<T, TId> : IRepositoryUnit<T, TId> where T : EntityObject
    {
        protected ObjectContext ObjectContext { get; private set; }

        protected string EntitySetName { get; private set; }
        protected string[] IncludePaths { get; private set; }

        public EntityRepositoryUnit(ObjectContext context)
            : this(context, null)
        {
        }

        public EntityRepositoryUnit(ObjectContext context, string[] includePaths)
        {
            this.ObjectContext = context;
            this.IncludePaths = includePaths;

            FetchEntitySetName();
        }

        //looks for an IQueryable<TEntity> property in the ObjectContext
        //and gets its name to be used in other methods
        private void FetchEntitySetName()
        {
            var entitySetProperty = this.ObjectContext.GetType().GetProperties().Single(p => p.PropertyType.IsGenericType && typeof(IQueryable<>).MakeGenericType(typeof(T)).IsAssignableFrom(p.PropertyType));

            this.EntitySetName = entitySetProperty.Name;
        }

        protected IQueryable<T> BuildQuery(ObjectQuery<T> q)
        {
            if (IncludePaths != null)
            {
                foreach (string path in IncludePaths)
                {
                    q = q.Include(path);
                }
            }

            return q;

        }

        protected IQueryable<T> Query()
        {
            return Query(null);
        }

        protected IQueryable<T> Query(Expression<Func<T, bool>> predicate)
        {
            var entitySet = String.Format("[{0}]", this.EntitySetName);
            ObjectQuery<T> baseQuery = this.ObjectContext.CreateQuery<T>(entitySet);

            var q = BuildQuery(baseQuery);

            if (predicate != null)
            {
                q = q.Where(predicate);
            }

            return q;
        }

        #region IRepositoryUnit<T, TId> Members

        public void Create(T objectToCreate)
        {
            ObjectContext.AddObject(EntitySetName, objectToCreate);
            ObjectContext.SaveChanges();
        }

        public void Delete(TId id)
        {
            var original = Get(id);
            ObjectContext.DeleteObject(original);
            ObjectContext.SaveChanges();
        }

        public abstract void Edit(T objectToUpdate);
        /*{
            PropertyInfo pi = objectToUpdate.GetType().GetProperty("Id");

            object id = pi.GetValue(objectToUpdate, null);
            var original = Get((TId)id);

            /////ObjectContext.ApplyReferencePropertyChanges(original, objectToUpdate);
            /////ObjectContext.ApplyPropertyChanges(EntitySetName, objectToUpdate);
            ObjectContext.SaveChanges();
            //return objectToUpdate;
        }*/

        public abstract T Get(TId id);/*
        {
            string queryString = string.Format(@"SELECT VALUE x FROM [{0}] as x WHERE x.Id = @Id", EntitySetName);

            var q = ObjectContext.CreateQuery<T>(queryString, new ObjectParameter("Id", id));

            return BuildQuery(q).FirstOrDefault();
        }*/

        public T Find(Expression<Func<T, bool>> predicate)
        {
            return Query().FirstOrDefault(predicate);
        }

        public IEnumerable<T> List()
        {
            return Query().ToList();
        }

        public PagedList<T> List(int? page, int pageSize)
        {
            return Query().ToPagedList(page.GetValueOrDefault(1), pageSize);
        }

        public PagedList<T> FindList(int? page, int pageSize, Expression<Func<T, bool>> predicate, Expression<Func<T, string>> orderBy, bool ascending)
        {
            return Query(predicate).ToPagedList(page.GetValueOrDefault(1), pageSize);
        }

        public IEnumerable<T> FindList(Expression<Func<T, bool>> predicate)
        {
            return Query(predicate).ToList();
        }
        #endregion


        public void SaveChanges()
        {
            ObjectContext.SaveChanges();
        }

        public void Detach(T objectToDetach)
        {
            throw new NotImplementedException();
        }
    }
}

